#include <string.h>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include "..\LogLib\log.h"
#include "ProtolProcess.h"


extern std::string g_strRecvData;

void nvt(int server,unsigned char data);
enum _ansi_state
{
    as_normal,
    as_esc,
    as_esc1
};

void ansi(int server,unsigned char data);

void ddww_error(int server,_verb verb,_option option);
void ddww_echo(int server,_verb verb, _option option);
void ddww_supp(int server,_verb verb,_option option); //Suppress GA
void ddww_term(int server,_verb verb,_option option); //Subnegotiate terminal type
void sbproc_term(int server,unsigned char data);


#define NUL     0
#define BEL     7
#define BS      8
#define HT      9
#define LF     10
#define VT     11
#define FF     12
#define CR     13
#define SE    240
#define NOP   241
#define DM    242
#define BRK   243
#define IP    244
#define AO    245
#define AYT   246
#define EC    247
#define EL    248
#define GA    249
#define SB    250
#define WILL  251
#define WONT  252
#define DO    253
#define DONT  254
#define IAC   255

typedef void(*LPOPTIONPROC)(int,_verb,_option);
typedef void(*LPDATAPROC)(int,unsigned char data);

inline void yesreply(int server, _verb verb,_option option)
{
    unsigned char buf[3];
    buf[0] = IAC;
    buf[1] = (verb==verb_do)?WILL:(verb==verb_dont)?WONT:(verb==verb_will)?DO:DONT;
    buf[2] = (unsigned char)option;
    send(server,(char*)buf,3,0);
}

inline void noreply(int server, _verb verb,_option option)
{
    unsigned char buf[3];
    buf[0] = IAC;
    buf[1] = (verb==verb_do)?WONT:(verb==verb_dont)?WILL:(verb==verb_will)?DONT:DO;
    buf[2] = (unsigned char)option;
    send(server,(char*)buf,3,0);
}

inline void askfor(int server, _verb verb,_option option)
{
    unsigned char buf[3];
    buf[0] = IAC;
    buf[1] = (unsigned char)verb;
    buf[2] = (unsigned char)option;
    send(server,(char*)buf,3,0);
}


void ddww_error(int server,_verb verb,_option option)
{
    switch(verb)
    {
    case verb_will:
        noreply(server,verb,option);
        break;
    case verb_wont:
        return;
    case verb_do:
        noreply(server,verb,option);
        break;
    case verb_dont:
        return;
    }
}
void ddww_echo(int server,_verb verb, _option option)
{
    switch(verb)
    {
    case verb_will:
    case verb_wont:
    case verb_dont:
        break;
    case verb_do:   // server wants me to loopback
        noreply(server,verb,option);
        return;
    }
    yesreply(server,verb,option);
}


void ddww_supp(int server,_verb verb,_option option) //Suppress GA
{
    switch(verb)
    {
        case verb_will: // server wants to suppress GA's
        {
            askfor(server,verb_do,TOPT_SUPP);
            askfor(server,verb_will,TOPT_SUPP);
            askfor(server,verb_do,TOPT_ECHO);
            break;
        }
        case verb_wont: // server wants to send GA's
        case verb_do:   // server wants me to suppress GA's
        case verb_dont:
        {
            break;
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
// Option TERMINAL-TYPE

void ddww_term(int server,_verb verb,_option option) //Subnegotiate terminal type
{
    switch(verb)
    {
    case verb_will:
        noreply(server,verb,option); // I don't want terminal info
        break;
    case verb_wont:
        //dat be cool - its not going to send. no need to confirm
        break;
    case verb_do:
        yesreply(server,verb,option); //I'll send it when asked
        break;
    case verb_dont://Ok - I won't
        break;
    }
}

// TERMINAL TYPE subnegotation
enum
{
    SB_TERM_IS = 0,
    SB_TERM_SEND = 1
};

#define NUM_TERMINALS 2

struct
{
    char* name;
    LPDATAPROC termproc;
    //pre requsites.
} terminal[NUM_TERMINALS] = {
    { "NVT", nvt },
    { "ANSI", ansi }
};

int term_index = 0;

void sbproc_term(int server,unsigned char data)
{

    if(data == SB_TERM_SEND)
    {
        if(term_index == NUM_TERMINALS)
            term_index = 0;
        else
            term_index++;
        char buf[16]; //pls limit
        buf[0] = IAC;
        buf[1] = SB;
        buf[2] = TOPT_TERM;
        buf[3] = SB_TERM_IS;
//        lstrcpy((LPSTR)&buf[4],(LPSTR)(terminal[(term_index==NUM_TERMINALS)?(NUM_TERMINALS-1):term_index].name));
        strcpy(&buf[4], terminal[(term_index == NUM_TERMINALS)?(NUM_TERMINALS-1):term_index].name);
        int nlen = strlen(&buf[4]);
        buf[4+nlen] = IAC;
        buf[5+nlen] = SE;
        send(server,buf,4+nlen+2,0);
    }
}

///////////////////////////////////////////////////////////////////////////////

struct
{
    _option option;
    LPOPTIONPROC OptionProc;
    LPDATAPROC DataProc;
}  ol[] = {
    {TOPT_ECHO,   ddww_echo,  NULL},
    {TOPT_SUPP,   ddww_supp,  NULL},
    {TOPT_TERM,   ddww_term,  sbproc_term},
    {TOPT_ERROR,  ddww_error, NULL}
};


void CProtolProcess::TelentProtcol(int server,unsigned char code)
{
    //These vars are the finite state
    static int state = state_data;
    static _verb verb = verb_sb;
    static LPDATAPROC DataProc = terminal[(term_index==NUM_TERMINALS)?(NUM_TERMINALS-1):term_index].termproc;

    //Decide what to do (state based)
    switch(state)
    {
        case state_data:
        {
            switch(code)
            {
                case IAC:
                {
                    state = state_code;
                    break;
                }
                default:
                    DataProc(server,code);
            }
            break;
        }

        case state_code:
        {
            state = state_data;
            switch(code)
            {
                // State transition back to data
                case IAC:
                DataProc(server,code);
                break;
                // Code state transitions back to data
                case SE:
                DataProc = terminal[(term_index==NUM_TERMINALS)?(NUM_TERMINALS-1):term_index].termproc;
                break;

                case NOP:
                break;

                case DM:
                break;

                case BRK:
                break;

                case IP:
                break;

                case AO:
                break;

                case AYT:
                break;

                case EC:
                break;

                case EL:
                break;

                case GA:
                break;

                // Transitions to option state
                case SB:
                verb = verb_sb;
                state = state_option;
                break;

                case WILL:
                verb = verb_will;
                state = state_option;
                break;

                case WONT:
                verb = verb_wont;
                state = state_option;
                break;

                case DO:
                verb = verb_do;
                state = state_option;
                break;

                case DONT:
                verb = verb_dont;
                state = state_option;
                break;
            }
            break;
        }


        case state_option:
        {
            state = state_data;

            //Find the option entry
            int i = 0;
            for(;ol[i].option != TOPT_ERROR && ol[i].option != code;i++);

            //Do some verb specific stuff
            if(verb == verb_sb)
                DataProc = ol[i].DataProc;
            else
                ol[i].OptionProc(server,verb,(_option)code);
            break;
        }

    }
}
void nvt(int server,unsigned char data)
{
    switch(data)
    {
    case 0:  //eat null codes.
        break;
    default: //Send all else to the console.
        break;
    }
}

void ansi(int server,unsigned char data)
{
    static _ansi_state state = as_normal;

    switch( state)
    {
    case as_normal:
        switch(data)
        {
        case 0:  //eat null codes.
            break;
        case 27: //ANSI esc.
            state = as_esc;
            break;
        default: //Send all else to the console.
            g_strRecvData += data;
            break;
        }
        break;
    case as_esc:
        state = as_esc1;
        break;
    case as_esc1:
        if(data > 64)
        {
            state = as_normal;
        }

        break;
    }
}
